package top.codedance.iotp.serialPortSDK.device.opw;

import top.codedance.iotp.common.util.ByteUtil;
import top.codedance.iotp.serialPortSDK.command.WdltCommand;
import top.codedance.iotp.serialPortSDK.entity.wdlt.CommonResult;
import top.codedance.iotp.serialPortSDK.entity.wdlt.WdltOilUnload;
import top.codedance.iotp.serialPortSDK.entity.wdlt.WdltOpw;
import top.codedance.iotp.serialPortSDK.entity.wdlt.WdltOpwAlarm;
import top.codedance.iotp.serialPortSDK.util.FloatUtil;
import top.codedance.iotp.serialPortSDK.util.HexUtil;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 维德路特
 * @Description
 * @date 2023/4/7 10:23
 */
public class VeederRoot_Opw extends AbstractOpw {
    /**
     * 报警数据
     */
    public static CommonResult alarmInfos = new CommonResult();
    /**
     * 卸油数据
     */
    public static CommonResult oilUnloadInfos = new CommonResult();
    /**
     * 液位数据
     */
    public static CommonResult opwInfos = new CommonResult();

    private VeederRoot_Opw() {
    }

    public static VeederRoot_Opw getInstance() {
        return new VeederRoot_Opw();
    }

    /**
     * 数据解析
     * @Description
     * @Param result
     * @return void
     * @author ChenPeng
     * @date 2023/4/19 14:54
     */
    @Override
    public void parse(String result) {
        result = result == null ? "" : result;
        String head = result.substring(0, 14);
        if (head.equals("01693230313030")) {
            opwConvert(result);
        }
        if (head.equals("01693230323030")) {
            oilUnloadingConvert(result);
        }
        if (head.equals("01693230363030")) {
            opwAlarmConvert(result);
        }
    }

    @Override
    public String genCmdForHeightOfOil() {
        return WdltCommand.INVENTORY;
    }

    public Map<String, Float> getHeightOfOil(List<String> tankNo) {
        Map<String, Float> resultMap = new HashMap<>();
        if (null == opwInfos) {
            return null;
        }
        List<WdltOpw> data = (List<WdltOpw>)opwInfos.getData();
        if (!data.isEmpty()) {
            Map<String, Float> collect = data.stream().collect(Collectors.toMap(WdltOpw::getTankNumber, WdltOpw::getHeight));
            resolvingToFloat(tankNo, resultMap, collect);
        }
        return resultMap;
    }

    @Override
    public String genCmdForHeight() {
        return WdltCommand.INVENTORY;
    }

    @Override
    public Map<String, Float> getHeight(List<String> tankNo) {
        Map<String, Float> resultMap = new HashMap<>();
        if (null == opwInfos) {
            return null;
        }
        List<WdltOpw> data = (List<WdltOpw>)opwInfos.getData();
        if (!data.isEmpty()) {
            Map<String, Float> collect = data.stream().collect(Collectors.toMap(WdltOpw::getTankNumber, WdltOpw::getHeight));
            resolvingToFloat(tankNo, resultMap, collect);
        }
        return resultMap;
    }

    /**
     * 共同数据解析
     * @Description
     * @Param tankNo
     * @Param resultMap
     * @Param collect
     * @return void
     * @author ChenPeng
     * @date 2023/4/19 18:33
     */
    private void resolvingToString(List<String> tankNo, Map<String, String> resultMap, Map<String, String> collect) {
        for (Map.Entry<String, String> entry : collect.entrySet()) {
            for (String tankCode : tankNo) {
                if (!tankNo.isEmpty() && tankNo.size() == 1 && Integer.valueOf(tankNo.get(0)) == 0) {
                    resultMap.put(entry.getKey(), entry.getValue());
                } else {
                    if (Integer.valueOf(entry.getKey()) == Integer.valueOf(tankCode)) {
                        resultMap.put(entry.getKey(), entry.getValue());
                    }
                }
            }
        }
    }

    /**
     * 共同数据解析
     * @Description
     * @Param tankNo
     * @Param Map<String, Float> resultMap
     * @Param Map<String, Float> collect
     * @return void
     * @author ChenPeng
     * @date 2023/4/19 18:33
     */
    private void resolvingToFloat(List<String> tankNo, Map<String, Float> resultMap, Map<String, Float> collect) {
        for (Map.Entry<String, Float> entry : collect.entrySet()) {
            for (String tankCode : tankNo) {
                if (!tankNo.isEmpty() && tankNo.size() == 1 && Integer.valueOf(tankNo.get(0)) == 0) {
                    resultMap.put(entry.getKey(), entry.getValue());
                } else {
                    if (Integer.valueOf(entry.getKey()) == Integer.valueOf(tankCode)) {
                        resultMap.put(entry.getKey(), entry.getValue());
                    }
                }
            }
        }
    }

    @Override
    public String genCmdForWater() {
        return WdltCommand.INVENTORY;
    }

    @Override
    public Map<String, Float> getWater(List<String> tankNo) {
        Map<String, Float> resultMap = new HashMap<>();
        if (null == opwInfos) {
            return null;
        }
        List<WdltOpw> data = (List<WdltOpw>)opwInfos.getData();
        if (!data.isEmpty()) {
            Map<String, Float> collect = data.stream().collect(Collectors.toMap(WdltOpw::getTankNumber, WdltOpw::getWater));
            resolvingToFloat(tankNo, resultMap, collect);
        }
        return resultMap;
    }

    @Override
    public String genCmdForTemperature() {
        return WdltCommand.INVENTORY;
    }

    @Override
    public Map<String, Float> getTemperature(List<String> tankNo) {
        Map<String, Float> resultMap = new HashMap<>();
        if (null == opwInfos) {
            return null;
        }
        List<WdltOpw> data = (List<WdltOpw>)opwInfos.getData();
        if (!data.isEmpty()) {
            Map<String, Float> collect = data.stream().collect(Collectors.toMap(WdltOpw::getTankNumber, WdltOpw::getTemperature));
            resolvingToFloat(tankNo, resultMap, collect);
        }
        return resultMap;
    }

    @Override
    public String genCmdForWaterVolume() {
        return WdltCommand.INVENTORY;
    }

    @Override
    public Map<String, Float> getWaterVolume(List<String> tankNo) {
        Map<String, Float> resultMap = new HashMap<>();
        if (null == opwInfos) {
            return null;
        }
        List<WdltOpw> data = (List<WdltOpw>)opwInfos.getData();
        if (!data.isEmpty()) {
            Map<String, Float> collect = data.stream().collect(Collectors.toMap(WdltOpw::getTankNumber, WdltOpw::getWaterVolume));
            resolvingToFloat(tankNo, resultMap, collect);
        }
        return resultMap;
    }

    @Override
    public String genCmdForVolume() {
        return WdltCommand.INVENTORY;
    }

    @Override
    public Map<String, Float> getVolume(List<String> tankNo) {
        Map<String, Float> resultMap = new HashMap<>();
        if (null == opwInfos) {
            return null;
        }
        List<WdltOpw> data = (List<WdltOpw>)opwInfos.getData();
        if (!data.isEmpty()) {
            Map<String, Float> collect = data.stream().collect(Collectors.toMap(WdltOpw::getTankNumber, WdltOpw::getVolume));
            resolvingToFloat(tankNo, resultMap, collect);
        }
        return resultMap;
    }

    @Override
    public String genCmdForAlarm() {
        return WdltCommand.ALARM_HISTORY;
    }

    @Override
    public Map<String, String> getAlarm(List<String> tankNo) {
        Map<String, String> resultMap = new HashMap<>();
        if (null == alarmInfos) {
            return null;
        }
        List<WdltOpwAlarm> data = (List<WdltOpwAlarm>)alarmInfos.getData();
        if (!data.isEmpty()) {
            Map<String, String> collect = data.stream().collect(Collectors.toMap(WdltOpwAlarm::getTankNumber, WdltOpwAlarm::getAlarmType));
            resolvingToString(tankNo, resultMap, collect);
        }
        return resultMap;
    }

    @Override
    public String genCmdForOilUnloading() {
        return WdltCommand.DELIVERY;
    }

    @Override
    public CommonResult getOilUnloadingInfos() {
        return oilUnloadInfos;
    }

    /**
     * 共通返回数据处理
     * @Description
     * @Param result
     * @return void
     * @author ChenPeng
     * @date 2023/4/18 18:07
     */
    private void commonDataConvert(String result) {
        alarmInfos.setReqCmd(result.substring(0, 14));
        // YYMMDDHHmm Current Date and Time
        List<String> dateStrList = HexUtil.hexStringToListString(result.substring(14, 24), 2);
        StringBuilder sb = new StringBuilder();
        sb.append(dateStrList.get(0))
                .append(dateStrList.get(1))
                .append(dateStrList.get(2))
                .append(dateStrList.get(3))
                .append(dateStrList.get(4));
        alarmInfos.setCurrentAt(FloatUtil.timeToData(sb.toString()));
        alarmInfos.setDataTerminationFlag(result.substring(result.length() - 6, result.length() - 4));
        alarmInfos.setMessageChecksum(result.substring(result.length() - 4, result.length()));
    }

    /**
     * 油罐报警 = 油罐报警历史报告
     * @Description Function Code: 206 Function Type: In-Tank Alarm History Report
     * @Param result
     * @return void
     * @author ChenPeng
     * @date 2023/4/19 14:49
     */
    public void opwAlarmConvert(String result) {
        List<WdltOpwAlarm> alarmList = new ArrayList<>();
        // 返回头01693230363030  具体数据34位
        if (result.substring(0, 14).equals("01693230363030")) {
            commonDataConvert(result);
            // 去头去尾后的返回数据
            List<String> resultList = new ArrayList<>();
            String packs = result.substring(24, result.length() - 8);
            for (int i = 0; i < packs.length(); i += 18) {
                if (packs.length() <= i + 18) {
                    break;
                }
                resultList.add(packs.substring(i, i + 18));
            }
            // 数据解析
            for (String item : resultList) {
                WdltOpwAlarm alarmInfo = new WdltOpwAlarm();
                // 罐号 TT - Tank Number (Decimal, 00=all)
                alarmInfo.setTankNumber(String.valueOf(HexUtil.HexToInt(item.substring(0, 2))));
                // 储罐历史报警次数（十进制，00=无）nn - Number of alarms in history for tank (Decimal, 00=none)
                alarmInfo.setAlarmHistoryNumber(item.substring(2, 4));
                // 报警发生的日期和时间 Date and time alarm occurred
                List<String> alarmDateStrList = HexUtil.hexStringToListString(item.substring(4, 14), 2);
                StringBuilder alarmSb = new StringBuilder();
                alarmSb.append(alarmDateStrList.get(0))
                        .append(alarmDateStrList.get(1))
                        .append(alarmDateStrList.get(2))
                        .append(alarmDateStrList.get(3))
                        .append(alarmDateStrList.get(4));
                alarmInfo.setAlarmOccurredTime(FloatUtil.timeToData(alarmSb.toString()));
                // 报警类型 Type of alarm
                alarmInfo.setAlarmType(item.substring(14, 18));
                alarmList.add(alarmInfo);
            }
            alarmInfos.setData(alarmList);
        }
    }

    /**
     * 卸油 = 储罐内交付报告
     * @Description Function Code: 202 Function Type: In-Tank Delivery Report
     * @Param result
     * @return void
     * @author ChenPeng
     * @date 2023/4/19 14:49
     */
    public void oilUnloadingConvert(String result) {
        List<WdltOilUnload> oilUnloadList = new ArrayList<>();
        // 01693230323030 = 请求头 = 请求命令 具体数据34位
        if (result.substring(0, 14).equals("01693230323030")) {
            commonDataConvert(result);
            // 生成数据数组
            List<String> resultList = bodyGenArrays(result, 15,27);
            // 数据解析
            for (String item : resultList) {
                WdltOilUnload oil = new WdltOilUnload();
                // 2. TT - Tank Number (Decimal, 00=all)
                oil.setTankNumber(String.valueOf(HexUtil.HexToInt(item.substring(0, 2))));
                // 3. p - Product Code (one ASCII character [20h-7Eh])
                oil.setProductCode(""+ HexUtil.HexToInt(HexUtil.asciiToString(item.substring(2, 3))));
                // 4. dd - Number of Deliveries to follow (Decimal, 00 if no data available for this tank)
                oil.setDeliveriesNumber(item.substring(3, 5));
                // 5. YYMMDDHHmm - Starting Date/Time
                List<String> startTimeStr = HexUtil.hexStringToListString(item.substring(5, 15),2);
                StringBuilder sbStartTime = new StringBuilder();
                sbStartTime.append(startTimeStr.get(0))
                        .append(startTimeStr.get(1))
                        .append(startTimeStr.get(2))
                        .append(startTimeStr.get(3))
                        .append(startTimeStr.get(4));
                oil.setStartAt(FloatUtil.timeToData(sbStartTime.toString()));
                // 6. YYMMDDHHmm - Ending Date/Time
                List<String> endTimeStr = HexUtil.hexStringToListString(item.substring(15, 25),2);
                StringBuilder endSb = new StringBuilder();
                endSb.append(endTimeStr.get(0))
                        .append(endTimeStr.get(1))
                        .append(endTimeStr.get(2))
                        .append(endTimeStr.get(3))
                        .append(endTimeStr.get(4));
                oil.setEndAt(FloatUtil.timeToData(endSb.toString()));
                // 7. NN - Number of eight character Data Fields to follow (Hex)
                // 封装卸油数据 FFFFFFFF - ASCII Hex IEEE floats:
                int Nn = HexUtil.HexToInt(item.substring(7, 9));
                oil.setCharacterData("" + Nn);
                genOilUnloadBody(Nn, oil, item);
                oilUnloadList.add(oil);
            }
            oilUnloadInfos.setData(oilUnloadList);
        }
    }

    /**
     * 封装卸油数据 FFFFFFFF - ASCII Hex IEEE floats:
     * @param Nn
     * @param oil
     * @param item
     */
    private void genOilUnloadBody(int Nn, WdltOilUnload oil, String item) {
        if (Nn >= 1) {
            oil.setStartingVolume(ByteUtil.toFloat(ByteUtil.toByte(item.substring(27, 35))));
        }
        if (Nn >= 2) {
            oil.setStartingTcVolume(ByteUtil.toFloat(ByteUtil.toByte(item.substring(35, 43))));
        }
        if (Nn >= 3) {
            oil.setStartingWater(ByteUtil.toFloat(ByteUtil.toByte(item.substring(43, 51))));
        }
        if (Nn >= 4) {
            oil.setStartingTemp(ByteUtil.toFloat(ByteUtil.toByte(item.substring(51, 59))));
        }
        if (Nn >= 5) {
            oil.setEndingVolume(ByteUtil.toFloat(ByteUtil.toByte(item.substring(59, 67))));
        }
        if (Nn >= 6) {
            oil.setEndingTcVolume(ByteUtil.toFloat(ByteUtil.toByte(item.substring(67, 75))));
        }
        if (Nn >= 7) {
            oil.setEndingWater(ByteUtil.toFloat(ByteUtil.toByte(item.substring(75, 83))));
        }
        if (Nn >= 8) {
            oil.setEndingTemp(ByteUtil.toFloat(ByteUtil.toByte(item.substring(83, 91))));
        }
        if (Nn >= 9) {
            oil.setStartingHeight(ByteUtil.toFloat(ByteUtil.toByte(item.substring(91, 99))));
        }
        if (Nn >= 10) {
            oil.setEndingHeight(ByteUtil.toFloat(ByteUtil.toByte(item.substring(99, 107))));
        }
    }

    /**
     * 生成数据数组
     * @Description
     * @Param result
     * @Param NnStart NN的开始位
     * @Param NnEnd NN的结束位
     * @return java.util.List<java.lang.String>
     * @author ChenPeng
     * @date 2023/4/21 16:19
     */
    private List<String> bodyGenArrays(String result,int NnStart, int NnEnd) {
        List<String> resultList = new ArrayList<>();
        String packs = result.substring(24, result.length() - 8);
        int Nn = 0; // (NN)
        int bodyTotal = 0;// 每个数组数据总长度
        for (int i = 0; i < packs.length(); i += bodyTotal) {
            Nn = HexUtil.HexToInt(packs.substring(bodyTotal + NnStart, bodyTotal + NnEnd));
            // 总长度 = TT到NN的长度 * IEEE的长度
            bodyTotal = NnEnd + Nn * 8;
            // 容错处理：如果所剩位数不足以形成一条记录则不做处理
            if (packs.length() <= bodyTotal) {
                break;
            }
            resultList.add(packs.substring(i, i + bodyTotal));
        }
        return resultList;
    }

    /**
     * 液位数据转换
     * @Description Function Code: 201 Function Type: In-Tank Inventory Report
     * @Param result
     * @return void
     * @author ChenPeng
     * @date 2023/4/19 14:48
     */
    public void opwConvert(String result) {
        List<WdltOpw> opwList = new ArrayList<>();
        if (result.substring(0, 14).equals("01693230313030")) {
            commonDataConvert(result);
            // 生成数据数组
            List<String> resultList = bodyGenArrays(result, 7, 9);
            // 数据解析
            for (String item : resultList) {
                WdltOpw opw = new WdltOpw();
                // 2. TT - Tank Number (Decimal, 00=all)
                opw.setTankNumber(String.valueOf(HexUtil.HexToInt(item.substring(0, 2))));
                // 3. p - Product Code (one ASCII character [20h-7Eh])
                opw.setProductCode("" + HexUtil.HexToInt(HexUtil.asciiToString(item.substring(2, 3))));
                // 4. ssss - Tank Status Bits: Bit 1 - (LSB) Delivery in Progress Bit 2 - Leak Test in Progress Bit 3 - Invalid Fuel Height Alarm (MAG Probes Only) Bit 4-16 - Unused
                String status = new StringBuffer(HexUtil.hexTo2String(item.substring(3, 7))).reverse().toString();
                opw.setTankStatus((status.substring(0, 1)).equals("0") ? "false" : "true");
                // 5. NN - Number of eight character Data Fields to follow (Hex)
                int Nn = HexUtil.HexToInt(item.substring(7, 9));
                opw.setCharacterData("" + Nn);
                // 封装液位数据 FFFFFFFF - ASCII Hex IEEE floats:
                genOpwBody(Nn, opw, item);
                opwList.add(opw);
            }
            opwInfos.setData(opwList);
        }
    }

    // 封装液位数据 FFFFFFFF - ASCII Hex IEEE floats:
    private void genOpwBody(int Nn, WdltOpw opw, String item) {
        if (Nn >= 1) {
            opw.setVolume(ByteUtil.toFloat(ByteUtil.toByte(item.substring(9, 17))));
        }
        if (Nn >= 2) {
            opw.setTcVolume(ByteUtil.toFloat(ByteUtil.toByte(item.substring(17, 25))));
        }
        if (Nn >= 3) {
            opw.setUllage(ByteUtil.toFloat(ByteUtil.toByte(item.substring(25, 33))));
        }
        if (Nn >= 4) {
            opw.setHeight(ByteUtil.toFloat(ByteUtil.toByte(item.substring(33, 41))));
        }
        if (Nn >= 5) {
            opw.setWater(ByteUtil.toFloat(ByteUtil.toByte(item.substring(41, 49))));
        }
        if (Nn >= 6) {
            opw.setTemperature(ByteUtil.toFloat(ByteUtil.toByte(item.substring(49, 57))));
        }
        if (Nn >= 7) {
            opw.setWaterVolume(ByteUtil.toFloat(ByteUtil.toByte(item.substring(57, 65))));
        }
    }

    /*
     * 根据报警事件类型返回事件消息
     * @Description
     * @Param param
     * @return java.lang.String
     * @author ChenPeng
     * @date 2023/4/13 17:03
     */
    public static String getWdltAlarmDesc(String param) {
        Map<String, String> alarmDataList = new HashMap<>();
        alarmDataList.put("000A", "水箱高水位警告");
        alarmDataList.put("000B", "需要储罐输送警告");
        alarmDataList.put("000C", "储罐最大产品警报");
        alarmDataList.put("000D", "储罐总泄漏测试失败警报");
        alarmDataList.put("000E", "储罐定期泄漏测试失败警报");
        alarmDataList.put("000F", "储罐年度泄漏测试失败警报");
        alarmDataList.put("0010", "需要储罐定期测试警告");
        alarmDataList.put("0011", "需要储罐年度测试警告");
        alarmDataList.put("0012", "储罐定期测试需要报警");
        alarmDataList.put("0013", "储罐年度测试需要报警");
        alarmDataList.put("0014", "储罐泄漏测试激活");
        alarmDataList.put("0015", "油箱无CSLD怠速时间警告");
        alarmDataList.put("0016", "储罐虹吸破裂激活警告");
        alarmDataList.put("0017", "储罐CSLD速率增加警告");
        alarmDataList.put("0018", "储罐AccuChart校准警告");
        alarmDataList.put("0019", "储罐人力资源管理调节警告");
        alarmDataList.put("001A", "储罐HRM调节警报");
        alarmDataList.put("001B", "储罐低温警告");
        alarmDataList.put("001C", "储罐遗失提货单警告");
        alarmDataList.put("001D", "储罐/管线总泄漏警报");
        return alarmDataList.get(param);
    }
}